package cloudspace.ui.applet;

import java.applet.Applet;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import netscape.javascript.JSException;
import netscape.javascript.JSObject;
import cloudspace.ui.applet.AppletConsoleMultiplexer.PrintType;
import cloudspace.ui.applet.io.CloudFile;
import cloudspace.ui.applet.io.CloudFileInputStream;
import cloudspace.ui.applet.io.CloudFileOutputStream;
import cloudspace.ui.applet.lang.CloudSystem;


public class AppletVM
{
    private static Applet applet;

    private static String uuid;

    public static PrintStream out;

    public static PrintStream err;


    public static void init( Applet myApplet )
    {
        uuid = myApplet.getParameter( "uuid" );
        out = new AppletConsoleMultiplexer( System.out, PrintType.OUT );
        err = new AppletConsoleMultiplexer( System.err, PrintType.ERROR );
        applet = myApplet;
        evalJScript( "function systemOut(reqId, output) {"
            + "var data = {};"
            + "data['reqId'] = reqId;"
            + "data['message'] = output;"
            + "zAu.send(new zk.Event(null, 'onSystemOut', data, {toServer:true}));"
            + "}" );
        evalJScript( "function systemErr(reqId, output) {"
            + "var data = {};"
            + "data['message'] = output;"
            + "zAu.send(new zk.Event(null, 'onSystemErr', data, {toServer:true}));"
            + "}" );
        evalJScript( "function getApplet() {"
            + "return document.getElementById('" + uuid + "');}" );
        evalJScript( CloudFileInputStream.getJavaScript() );
        evalJScript( CloudFileOutputStream.getJavaScript() );
        evalJScript( CloudFile.getJavaScript() );
        evalJScript( CloudSystem.getJavaScript() );
        evalJScript( AppletApplicationSupportStrategy.getJavaScript());
        evalJScript(AppletDistributedMap.getJavaScript());
    }


    public static void pushError( String msg )
    {
        callJScriptFunc( "systemErr", msg );

    }


    public static void pushOut( String msg )
    {
        callJScriptFunc( "systemOut", msg );

    }


    public static void evalJScript( List<String> toEval )
    {
        for ( String eval : toEval )
            evalJScript( eval );
    }


    public static void evalJScript( String toEval )
    {
//         System.out.println( "Evaling " + toEval );
        JSObject jso = null;
        try
        {
            jso = JSObject.getWindow( applet );
            jso.eval( toEval );
        }
        catch ( JSException ex )
        {
            ex.printStackTrace();
        }
    }


    public static CloudResult callJScriptFunc( String fn, Object... params )
    {
        UUID reqId = UUID.randomUUID();
        ArrayList<Object> param = new ArrayList<Object>();
//         System.out.print( "Calling " + fn + " with " );
        param.add( reqId.toString() );
//         System.out.println(reqId.toString()+" ");
        for ( Object entry : params )
        {
            param.add( entry.toString() );
//             System.out.print( entry+" " );
        }
//         System.out.println();
        JSObject jso = null;
        try
        {
            jso = JSObject.getWindow( applet );
            CloudResult response = null;
            Object lock = new Object();
            haltedThreads.put( reqId.toString(), lock );
            synchronized ( lock )
            {
                jso.call( fn, param.toArray() );
                try
                {
                    lock.wait();
                    response = (CloudResult)haltedThreads.remove( reqId.toString() );
                }
                catch ( InterruptedException e )
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

            return response;
        }
        catch ( JSException ex )
        {
            ex.printStackTrace();
        }
        return null;
    }


    private static class ResponseHandler extends Thread
    {
        private String reqId;

        private CloudResult response;


        public ResponseHandler( String reqId, CloudResult response )
        {
            this.reqId = reqId;
            this.response = response;
        }


        public void run()
        {
            Object lock = haltedThreads.get( reqId );
            if ( reqId == null || lock == null )
            {
                return;
            }
            synchronized ( lock )
            {
                haltedThreads.put( reqId, response );
                lock.notify();
            }
        }
    }


    public static void gotResponse( String reqId, CloudResult response )
    {
        Thread responseThread = new ResponseHandler( reqId, response );
        responseThread.start();
    }

    private static Map<String, Object> haltedThreads = new HashMap<String, Object>();

}
